package diana;

import java.util.*;

import Algoritmos.GeneradorDeRutas;
import Algoritmos.Solver;

import estructuras.*;
import estructuras.common.*;

public class DSolver extends Solver {
	
	final static String NOMBRE = "Algoritmo de Diana";
	
	public DSolver() {
		super(NOMBRE);
	}
	
	@Override
	protected Ruta generaRutaDePaquete(Paquete paquete) {
		Almacen origen = paquete.getOrigen();
		Almacen destino = paquete.getDestino();
		Vector<Ruta> posiblesRutas = new Vector<>();
		Fecha fechaRegistro = paquete.getFechaRegistro();
		if(origen.getContinente() == destino.getContinente()){
			List<Almacen> almacenes = origen.getContinente().getAlmacenes();
			//directo con maximo 12 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasDirectas(paquete, origen, destino, fechaRegistro, new Fecha(0, 12)));
			//una escala con 0 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenes));
		}else{
			List<Almacen> almacenesOrigen = origen.getContinente().getAlmacenes();
			List<Almacen> almacenesDestino = origen.getContinente().getAlmacenes();
			//un vuelo directo con maximo 24 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasDirectas(paquete, origen, destino, fechaRegistro, new Fecha(1, 0)));
			//un vuelo intercon + 2 intra con 0 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesDestino, almacenesDestino));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesOrigen, almacenesDestino));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesOrigen, almacenesOrigen));
			//un vuelo inter + 1 intra con maximo 12 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 12), almacenesOrigen));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 12), almacenesDestino));
		}
		
		if(posiblesRutas.isEmpty() || posiblesRutas.firstElement().getBottleNeck() == 0) return null;
		else{
			Random random = new Random();
			int index = random.nextInt(posiblesRutas.size());
			Ruta ruta = posiblesRutas.get(index);
			ruta.ejecutaRuta();
			return ruta;
		}
	}

	@Override
	public List<Paquete> generaPaquetesEstimadosParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin){
		//si no hay distribuciones, retornamos null
		if(distribuciones.size() != almacenes.size() * (almacenes.size() - 1)) return null;
		
		LinkedList<Paquete> agregados = new LinkedList<Paquete>();
		for(Almacen a: this.almacenes)
			for(Almacen b : this.almacenes)
				if(a != b)
					for(Fecha f = fechaInicio; f.compareTo(fechaFin) < 0; f = Fecha.nextHora(f)){
						Distribucion distribucion = distribuciones.get(new AbstractMap.SimpleEntry<Almacen, Almacen>(a, b));
						int paquetes = distribucion.getRandom(f.getDiaSemana(), f.getHora());
						while(paquetes-- > 0){
							Paquete p = new Paquete(a, b, f, false);
							addPaqueteEstimado(p);
							agregados.add(p);
					}
		}
		return agregados;
	}
	
	/*void generaDistribuciones(Fecha fechaLimite){
		this.distribuciones = new HashMap<>();
		for(Almacen a : this.almacenes)
			for(Almacen b : this.almacenes)
				if(a != b)
					distribuciones.put(new AbstractMap.SimpleEntry<Almacen, Almacen>(a, b), new DistribucionNormalConexion(1, 1));
	}*/

	@Override
	public void generaYEjecutaRutasParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin) {
		Vector<Paquete> paquetesOrdenados = this.getPaquetesEnIntervaloDeTiempo(fechaInicio, fechaFin);
		for(Paquete paquete : paquetesOrdenados){
			if(this.generaRutaDePaquete(paquete) == null){
				this.addPaqueteSinRuta(paquete);
			}
		}
		System.out.println( "Numero de paquetes sin ruta: " + this.paquetesSinRuta.size() );
		System.out.println( "Numero de paquetes totales: " + paquetesOrdenados.size());
	}

	@Override
	public void addDistribucion(Almacen origen, Almacen destino, int media,
			double varianza) {
		distribuciones.put(new AbstractMap.SimpleEntry<Almacen, Almacen>(origen, destino),
				new DistribucionNormalConexion(media, varianza));
	}
	
}
